/*
Copyright (C) 2015 Marien Raat, Pieter Staal

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <sfmlFrontend.h>

using namespace sf;

SFMLFrontend::SFMLFrontend(unsigned screenSizeX, unsigned screenSizeY)
{
    screenSize = Vector2u(screenSizeX, screenSizeY);
    window = new RenderWindow(VideoMode(screenSize.x, screenSize.y), "Float");

#ifndef OS_WIN
    randomDevice = new std::random_device;
    mt = new std::mt19937((*randomDevice)());
#else
    mt = new std::mt19937(time(0));
#endif

    islandHeightmapGenerator = new IslandHeightmapGenerator(mt);

    mapDisplayer = new MapDisplayer(screenSize, MAP_DISPLAY_TYPE_HEIGHTMAP);

    markovNames = new MarkovNames("data/names.txt", 2, mt);
    mapDisplayer->setIslandName(markovNames->createName());

    lightingGenerator = new LightingGenerator;
    useMouseLamp = false;
    mouseLampHeight = 1.0f * pow(10.0f, 3.0f);

    int diamondSquareSize = (int) pow(2.0, ceil
                                  (log(std::max(screenSize.x, screenSize.y) - 1) /
                                   log(2.0))) + 1,
        ammountOfPasses = (int) (log(diamondSquareSize) /
                                 log(2.0));
    heightmapModifiers = new double[ammountOfPasses];
    double modifiers[10] = {
        0.0, // 1024
        1.0, // 512
        0.9, // 256
        0.55, // 128
        0.35, // 64
        0.2, // 32
        0.1, // 16
        0.05, // 8
        0.02, // 4
        0.004, // 2
    };
    if (ammountOfPasses > 10) {
        for (int i = 0; i < ammountOfPasses; i++) {
            heightmapModifiers[i] = i < (ammountOfPasses - 10) ?
                                        0.0 : modifiers[i - (ammountOfPasses - 10)];
        }
    } else {
        for (int i = 10 - ammountOfPasses; i < 10; i++) heightmapModifiers[i] = modifiers[i];
    }
    islandHeightmapGenerator->initializeMap(screenSize.x, screenSize.y,
                                            heightmapModifiers);

    landMapGenerator = new LandMapGenerator;
    waterLevel = islandHeightmapGenerator->modifiedHeightValue(0.45);

    waterSettings.size = screenSize.x;
    waterSettings.waterLevel = waterLevel;
    waterSettings.landmap = landMapGenerator->landMap;
    waterSettings.heightmap = islandHeightmapGenerator->islandMap;

    waterGenerator = new WaterGenerator(waterSettings);

    landMapGenerator->setWaterLevel(waterLevel);
    mapDisplayer->setHeightmap(islandHeightmapGenerator->islandMap);
    mapDisplayer->setWatermap(waterGenerator->getWatermap());
    mapDisplayer->setWaterLevel(waterLevel);
    mapDisplayer->setHeightModifierFunction(islandHeightmapGenerator->modifiedHeightValue);
}

void SFMLFrontend::start()
{
    loadFiles();

    while (window->isOpen()) {
	handleEvents();
	if (focus)
	    handleInput();
	update();
	draw();
    }
    return;
}

void SFMLFrontend::loadFiles()
{
    if (!islandNameFont.loadFromFile("fonts/DejaVuSerif.ttf"))
	std::cout << "File \"fonts/DejaVuSerif.ttf\" not found!" << std::endl
                  << "Check if you are running the program from the main project folder."
                  << std::endl;
    else
	mapDisplayer->setNamesFont(&islandNameFont);
}

void SFMLFrontend::handleEvents()
{
    Event event;
    // Only handle one of the KeyPressed events per cycle to prevent the events
    // of accumilating when a key is kept pressed.
    bool handledKeyPressedEvent = false;
    while (window->pollEvent(event)) {
	if (event.type == Event::Closed) {
	    window->close();
	}
	if (event.type == Event::KeyPressed && !handledKeyPressedEvent) {
	    handledKeyPressedEvent = true;
	    if (event.key.code == Keyboard::Escape) {
		window->close();
	    }
	    if (event.key.code == Keyboard::N) {
                islandHeightmapGenerator->initializeMap(screenSize.x, screenSize.y,
                                                        heightmapModifiers);
		mapDisplayer->setIslandName(markovNames->createName());
		updateHeightmapDisplay();
	    }
	    if (event.key.code == Keyboard::P) {
		islandHeightmapGenerator->pass();
		waterGenerator->normalizeMap();
		updateHeightmapDisplay();
	    }
	    if (event.key.code == Keyboard::F) {
                islandHeightmapGenerator->finishMap();
		updateHeightmapDisplay();
	    }
	    if (event.key.code == Keyboard::C) {
		if (islandHeightmapGenerator->isMapDone()) {
		    islandHeightmapGenerator->createNewMap(screenSize.x, screenSize.y,
                                                           heightmapModifiers);
		    mapDisplayer->setIslandName(markovNames->createName());
		} else {
                    islandHeightmapGenerator->finishMap();
                }

                waterGenerator->normalizeMap();
                updateHeightmapDisplay();
            }
            if (event.key.code == Keyboard::I) {
                mapDisplayer->setSmoothingType(SMOOTHING_TYPE_INTERPOLATE);
                updateHeightmapDisplay();
            }
            if (event.key.code == Keyboard::A) {
                mapDisplayer->setSmoothingType(SMOOTHING_TYPE_AVERAGE);
                updateHeightmapDisplay();
            }
            if (event.key.code == Keyboard::Num1) {
                mapDisplayer->setMapDisplayType(MAP_DISPLAY_TYPE_HEIGHTMAP);
                updateHeightmapDisplay();
            }
            if (event.key.code == Keyboard::Num2) {
                mapDisplayer->setMapDisplayType(MAP_DISPLAY_TYPE_SIMPLE_MAP);
                updateHeightmapDisplay();
            }
            if (event.key.code == Keyboard::Num3) {
                mapDisplayer->setMapDisplayType(MAP_DISPLAY_TYPE_LAND_MAP);
                updateHeightmapDisplay();
            }
            if (event.key.code == Keyboard::Num4) {
                mapDisplayer->setMapDisplayType(MAP_DISPLAY_TYPE_LIGHTED_MAP);
                updateHeightmapDisplay();
            }
            if (event.key.code == Keyboard::S) {
                // Write the contents of the current screen to "screenshot.png"
                Image screen = window->capture();
                screen.saveToFile("screenshot.png");
            }
            if (event.key.code == Keyboard::M &&
                mapDisplayer->getMapDisplayType()
                == MAP_DISPLAY_TYPE_LIGHTED_MAP) {
                while (Keyboard::isKeyPressed(Keyboard::M));
                useMouseLamp = !useMouseLamp;
                updateHeightmapDisplay();
            }
            if (event.type == Event::MouseWheelMoved &&
                mapDisplayer->getMapDisplayType() ==
                MAP_DISPLAY_TYPE_LIGHTED_MAP) {
                double delta = (double) event.mouseWheel.delta;
                mouseLampHeight *= pow(2.0, 0.1 * delta);
            }
            if (event.key.code == Keyboard::D) {
                mapDisplayer->setWaterDisplayed(!mapDisplayer->isWaterDisplayed());
            }
            if (event.key.code == Keyboard::E) {
                waterGenerator->updateWaterMap(512);
                mapDisplayer->setWatermap(waterGenerator->getWatermap());
                mapDisplayer->updateDisplay();
            }
            if (event.key.code == Keyboard::Q) {
                waterGenerator->updateWaterMap(1);
                mapDisplayer->setWatermap(waterGenerator->getWatermap());
                mapDisplayer->updateDisplay();
            }
            if (event.key.code == Keyboard::R) {
                waterGenerator->addRiver();
            }

            if (event.key.code == Keyboard::S) {
                // Write the contents of the current screen to "screenshot.png"
                Image screen = window->capture();
                screen.saveToFile("screenshot.png");
            }
        }

        // Focus control
        if (event.type == Event::LostFocus)
            focus = false;
        if (event.type == Event::GainedFocus)
            focus = true;
    }
}
void SFMLFrontend::handleInput()
{
    if (Keyboard::isKeyPressed(Keyboard::W))
    {
        waterGenerator->updateWaterMap(8);
        mapDisplayer->setWatermap(waterGenerator->getWatermap());
        mapDisplayer->updateDisplay();
    }
}

void SFMLFrontend::updateHeightmapDisplay()
{
    mapDisplayer->setHeightmapPassSize
	(islandHeightmapGenerator->getCurrentPassSize());
    mapDisplayer->updateHeightmapDisplay();
    mapDisplayer->setLandMap
        (landMapGenerator->generateLandMap
         (mapDisplayer->heightmapCopy,
          screenSize.x, screenSize.y, true));
    waterGenerator->setLandmap(landMapGenerator->landMap);
    if (mapDisplayer->getMapDisplayType()
        == MAP_DISPLAY_TYPE_LIGHTED_MAP) {
        mapDisplayer->updateHeightmapDisplay();
        double *cHeightmap = getClippedHeightmap(waterLevel);
        lightingGenerator->generateNormalMap(cHeightmap,
                                             screenSize.x,
                                             screenSize.y);
        delete cHeightmap;

        Light sun;
        float sunStrength = 0.6f;
        sun.color = {1.0f * sunStrength, 1.0f * sunStrength,
                     0.6f * sunStrength};
        sun.falloff = {0.0f, 0.0f, 0.0f};
        sun.position = {-1.0f * pow(10.0f, 3.0f), -1.0f * pow(10.0f, 3.0f),
                        1 * pow(10.0f, 4.0f)};

        Light mouseLamp;
        float mouseLampStrength = 0.6f;
        mouseLamp.color = {1.0f * mouseLampStrength,
                           1.0f * mouseLampStrength,
                           1.0f * mouseLampStrength};
        mouseLamp.falloff = {0.0f, 0.0f, 0.0f};

        Vector2i mousePosition = Mouse::getPosition(*window);
        mouseLamp.position = {(float) mousePosition.x,
                              (float) mousePosition.y,
                              (float) mouseLampHeight};

        std::vector<Light> lights;

        if (useMouseLamp)
            lights.push_back(mouseLamp);
        else
            lights.push_back(sun);

        float atmosphereStrength = 0.4f;
        Vec3f diffuse = {0.8f * atmosphereStrength,
                         0.8f * atmosphereStrength,
                         1.0f * atmosphereStrength};

        lightingGenerator->generateLightMap(lights, diffuse);

        mapDisplayer->setLightMap(lightingGenerator->lightmap);
    }
    mapDisplayer->updateDisplay();
}

void SFMLFrontend::update()
{
    if (mapDisplayer->getMapDisplayType()
        == MAP_DISPLAY_TYPE_LIGHTED_MAP &&
        useMouseLamp)
        updateHeightmapDisplay();
}

void SFMLFrontend::draw()
{
    window->clear();
    mapDisplayer->draw(window);
    window->display();
}

double *SFMLFrontend::getClippedHeightmap(double clipValue)
{
    double *clippedHeightmap = new double[screenSize.x * screenSize.y];
    for (int i = 0; i < screenSize.x * screenSize.y; i++)
        clippedHeightmap[i] = std::max(clipValue,
                                       mapDisplayer->heightmapCopy[i]);
    return clippedHeightmap;
}
